package com.cgs.query.handler.executor

import com.cgs.query.configuration.QueryConfiguration
import com.cgs.query.connection.ConnectionFactory
import com.cgs.query.domain.MappedStatement
import com.cgs.query.exception.QueryException
import com.cgs.query.executor.ExecutionContext
import com.cgs.query.executor.Executor
import com.cgs.query.handler.*
import com.cgs.query.handler.statement.RoutingStatementHandler
import mu.KotlinLogging
import java.sql.Connection
import java.sql.DriverManager
import java.sql.Statement

private val logger = KotlinLogging.logger { }

open class JdbcExecutor(
        private val config: QueryConfiguration,
        private val connectionFactory: ConnectionFactory
) : Executor, SecurityAdapter {

    override fun start() {
        TODO()
    }

    override fun isStart(): Boolean {
        TODO()
    }

    override fun close(forceRollback: Boolean) {
        TODO()
    }

    override fun isClosed(): Boolean {
        TODO()
    }

    init {
        DriverManager.setLoginTimeout(this.config.configProperties.connectionTimeout)
    }

    @Permission
    override fun execute(ms: MappedStatement, param: Map<String, Any?>?, timeout: Int?): Any {
        executeCheck(ms)
        val executorContext = ExecutionContext(ms, param ?: mutableMapOf())
        val connection = this.connectionFactory.getConnection(ms.source)
        executorContext.dbType = getDbType(connection)
        val statementHandler = RoutingStatementHandler(config, executorContext)
        logger.info { "执行定义：[${ms.wholeName}]" }
        var statementInstance: Statement? = null
        try {
            val statement = {
                statementHandler.prepare(connection).apply {
                    setStatementTimeout(this, timeout)
                    statementHandler.parameterize(this)
                }
            }
            statementInstance = statement()
            return statementHandler.execute(statementInstance)
        } finally {
            statementHandler.close(statementInstance)
            this.connectionFactory.returnConnection(connection)
        }
    }

    private fun executeCheck(ms: MappedStatement) {

        if (ms.executionObj.statement.isBlank()
                || ms.executionObj.renderType.isBlank()
                || ms.executionObj.statementType.isBlank()) {
            throw QueryException("执行信息不完整")
        }
    }

    private fun getDbType(connection: Connection): String {
        return connection.metaData.databaseProductName
    }

    private fun setStatementTimeout(stmt: Statement, queryTimeout: Int?) {
        if (queryTimeout != null && queryTimeout > 0) {
            stmt.queryTimeout = queryTimeout
            return
        }
        if (queryTimeout == null) {
            stmt.queryTimeout = this.config.configProperties.queryTimeout
        }
    }

    override fun adapter(methodName: String, args: Array<Any?>): PermissionObject? {
        return when (methodName) {
            "execute" -> {
                val projectName = (args[0] as MappedStatement).projectName
                PermissionObject(projectName).apply {
                    type = "queryProject"
                    run = true
                }
            }
            else -> null
        }
    }
}
